package com.umakr.ax.security;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.umakr.ax.core.ModuleInit;
import com.umakr.ax.security.core.*;
import com.umakr.ax.security.core.filter.JwtAuthenticationTokenFilter;
import com.umakr.ax.security.core.jwt.JwtConfigProperties;
import com.umakr.ax.security.core.jwt.JwtTokenUtils;
import com.umakr.ax.security.model.User;
import com.umakr.ax.security.service.UserService;
import com.umakr.ax.utils.WebUtil;
import io.swagger.annotations.ApiOperation;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.*;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;

/**
 * @author gx
 * @since 2017/4/24
 */
@EnableConfigurationProperties(JwtConfigProperties.class)
@Configuration
@EnableWebSecurity(debug = false)
public class SecurityConfig extends WebSecurityConfigurerAdapter implements ModuleInit{
    @Autowired(required = false)
    UserDetailsService userDetailsService;
    @Bean
    BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Bean
    AjaxAuthenticationProvider ajaxAuthenticationProvider(){
        return new AjaxAuthenticationProvider();
    }
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        if (userDetailsService != null) {
            auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
        } else {
            auth.inMemoryAuthentication().withUser("admin").roles("ADMIN", "USER").password("wfc*admin");
        }
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/static/**",
                "/ueditor/**",
                "/favicon.ico",
                "/webjars/**",
                "/app/**/**.js",
                "/login",
                "/api/system/user/login",
                "/api/userDetails/modifyPassword",
                "/api/sms/sendPhoneVerifyCode",
                "/api/sms/checkPhoneVerifyCode",
                "/api/userDetails/registerUser",
                "/api/userDetails/updateUserInfo",
                "/api/**",
                "/error","/ig",
                "/suser/reg");
        super.configure(web);
    }
    @Bean
    ResourceRoleMappingSecurityMetaSource resourceRoleMappingSecurityMetaSource(){
        return new ResourceRoleMappingSecurityMetaSource();
    }
    @Bean
    public JwtTokenUtils jwtTokenUtils(){
        return new JwtTokenUtils();
    }
    @Bean
    public JwtAuthenticationTokenFilter authenticationTokenFilterBean(){
        return new JwtAuthenticationTokenFilter();
    }
    @Bean
    SecurityAccessDecisionManager securityAccessDecisionManager(){
        return new SecurityAccessDecisionManager();
    }
    SecurityInterceptor securityInterceptor() throws Exception{
        final SecurityInterceptor filterSecurityInterceptor =
                new SecurityInterceptor();
        filterSecurityInterceptor.setRejectPublicInvocations(true);
        filterSecurityInterceptor.setAlwaysReauthenticate(false);
        filterSecurityInterceptor.setValidateConfigAttributes(true);
        filterSecurityInterceptor.setPublishAuthorizationSuccess(false);
        filterSecurityInterceptor.setObserveOncePerRequest(true);
        filterSecurityInterceptor.setSecurityMetadataSource(resourceRoleMappingSecurityMetaSource());
        filterSecurityInterceptor.setAuthenticationManager(authenticationManager());
        filterSecurityInterceptor.setAccessDecisionManager(securityAccessDecisionManager());
        return filterSecurityInterceptor;
    }


    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
        return new SimpleUrlAuthenticationSuccessHandler("/login/success");
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler() {
        return new SimpleUrlAuthenticationFailureHandler("/login/failure");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(securityInterceptor(), FilterSecurityInterceptor.class);
        http.headers().frameOptions().disable();
//        http.antMatcher("/**").authorizeRequests().and().
        http.authorizeRequests()
                .antMatchers(
                        "/error",
                        "/ueditor/**",
                         "/login",
                        "/api/system/user/login",
                        "/api/sms/sendPhoneVerifyCode",
                        "/api/sms/checkPhoneVerifyCode",
                        "/api/userDetails/modifyPassword",
                        "/api/userDetails/registerUser",
                        "/api/userDetails/updateUserInfo",
                        "/api/**",
                        "/api/web/**"
                ).permitAll()
                //.antMatchers("/**").hasRole("ADMIN")
                .anyRequest().authenticated();
        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                response.getWriter().write(SecurityStatusException.accessDeny());
            }
        });
    }
    private static void collectionRes(ApplicationContext rootAppContext){
        SqlSessionTemplate sqlSessionTemplate = WebUtil.getBean(SqlSessionTemplate.class);
        RequestMappingHandlerMapping requestMappingHandlerMapping = rootAppContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
        List<Map<String,Object>> sResList = Lists.newArrayList();
        if(handlerMethods != null){
            handlerMethods.forEach(new BiConsumer<RequestMappingInfo,HandlerMethod>(){
                @Override
                public void accept(RequestMappingInfo requestMappingInfo, HandlerMethod handlerMethod) {
                    // 读取接口定义注解
                    ApiOperation obj  = handlerMethod.getMethodAnnotation(ApiOperation.class);
                    if(obj!=null){
                        Map<String,Object> map = Maps.newHashMap();
                        for (String pattern : requestMappingInfo.getPatternsCondition().getPatterns()) {
                            map.put("res_name",obj.value());
                            map.put("res_type","LINK");
                            map.put("res_icon",null);
                            map.put("res_leaf",null);
                            map.put("res_status",1);
                            map.put("res_parent_id",null);
                            map.put("res_one_level",null);
                            map.put("res_two_level",null);
                            map.put("res_url",pattern);
                            String code = pattern.replaceFirst("/","").replaceAll("/",".");
                            map.put("res_code",code);
                            Integer count = sqlSessionTemplate.selectOne("com.umakr.ax.core.mapper.ResourcesMapper.queryResByResCode",code);
                            if(count == 0){
                                sResList.add(map);
                            }
                        }
                    }
                }
            });
            //执行批量插入
            if(!sResList.isEmpty()){
                sqlSessionTemplate.insert("com.umakr.ax.core.mapper.ResourcesMapper.saveRes",sResList);
            }

        }

    }
    @Override
    public void moduleInit(ApplicationContext rootAppContext) {
        ResourceRoleMappingSecurityMetaSource resourceRoleMappingSecurityMetaSource = rootAppContext.getBean(ResourceRoleMappingSecurityMetaSource.class);
        resourceRoleMappingSecurityMetaSource.structure();
        List<User> userList = WebUtil.getBean(UserService.class).queryList(ImmutableMap.of("account","superadmin"));
        if(userList.isEmpty()){
            User user = new User();
            user.setAccount("superadmin");
            user.setStatus(1);
            user.setPassword(WebUtil.getBean(BCryptPasswordEncoder.class).encode("sppassword"));
            WebUtil.getBean(UserService.class).update(user);
        }

        //收集资源
        collectionRes(rootAppContext);



    }
}
